/**
  ******************************************************************************
  * @file    controller_can.c
  ******************************************************************************
  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <poll.h>
#include <net/if.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <errno.h>

#include "controller_state.h"
#include "controller_can.h"

extern controller_state_logcb_t g_clogcb_handler;

static struct can_frame s_frame_data;

uint64_t controller_time(void)
{
    struct timeval time;

    memset(&time, 0, sizeof(struct timeval));
    gettimeofday(&time, NULL);

    return ((uint64_t)time.tv_sec * 1000 + (uint64_t)time.tv_usec / 1000);
}

void *controller_init(void)
{
    controller_handle_t *controller_handle = NULL;

    controller_handle = malloc(sizeof(controller_handle_t));
    if (controller_handle == NULL) {
        return NULL;
    }
    memset(controller_handle, 0, sizeof(controller_handle_t));

    controller_handle->send_timeout_ms = CONTROLLER_DEFINE_SEND_TIMEOUT;
    controller_handle->recv_timeout_ms = CONTROLLER_DEFINE_RECV_TIMEOUT;
    controller_handle->buffer_max_len = CONTROLLER_DEFINE_BUFFER_MAX_LEN;

    return controller_handle;
}

int32_t controller_setopt(void *p_handle, controller_option_t option, void *data)
{
    controller_handle_t *controller_handle = NULL;

    if (p_handle == NULL || data == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (option >= CONTROLLER_OPTION_MAX) {
        return STATE_PORT_INPUT_OUT_RANGE;
    }

    controller_handle = (controller_handle_t *)p_handle;

    switch(option)
    {
        case CONTROLLER_OPTION_NAME:
            controller_handle->name = malloc(strlen(data) + 1);
            if (controller_handle->name == NULL) {
                printf("malloc failed\n");
                return STATE_PORT_MALLOC_FAILED;
            }
            memset(controller_handle->name, 0, strlen(data) + 1);
            memcpy(controller_handle->name, data, strlen(data));
            break;
        case CONTROLLER_OPTION_SEND_TIMEOUT_MS:
            controller_handle->send_timeout_ms = (*(uint32_t *)data);
            break;
        case CONTROLLER_OPTION_RECV_TIMEOUT_MS:
            controller_handle->recv_timeout_ms = (*(uint32_t *)data);
            break;
        case CONTROLLER_OPTION_BUFFER_MAX_LEN:
            controller_handle->buffer_max_len = (*(uint32_t *)data);
            break;
        case CONTROLLER_OPTION_RECV_HANDLER:
            controller_handle->recv_handler = (controller_recv_handler_t)data;
            break;
        default:
            printf("unknown option\n");
            break;
    }

    return STATE_SUCCESS;
}

int32_t controller_disconnect(void *p_handle)
{
    controller_handle_t *controller_handle = NULL;

    if (p_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    controller_handle = (controller_handle_t *)p_handle;

    shutdown(controller_handle->fd, 2);
    close(controller_handle->fd);

    /* 关闭CAN总线 */
    can_do_stop(controller_handle->name);

    return STATE_SUCCESS;
}

int32_t controller_connect(void *p_handle, uint32_t bitrate)
{
    controller_handle_t *controller_handle = NULL;

    if (p_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    controller_handle = (controller_handle_t *)p_handle;
    
    /* 关闭CAN总线 */
    can_do_stop(controller_handle->name);

    /* 设置CAN总线波特率 */
    can_set_bitrate(controller_handle->name, bitrate);

    /* 设置CAN总线复位时间 */
    can_set_restart_ms(controller_handle->name, 250);

    /* 启动CAN总线 */
    can_do_start(controller_handle->name);

    int32_t res;
    res = controller_establish(controller_handle->name, PF_CAN, SOCK_RAW, CAN_RAW, &(controller_handle->fd));
    if(res < 0) {
        return res;
    }

    return STATE_SUCCESS;
}

int32_t controller_establish(char *can_name, int family, int socktype, int protocol, int *fd_out)
{
    int fd = 0;
    struct ifreq ifr;
    struct sockaddr_can addr;

    if (can_name == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    fd = socket(family, socktype, protocol);
    if (fd < 0) {
        printf("create socket error, errno: %d\n", errno);
        perror("create socket error");
        return STATE_PORT_NETWORK_SOCKET_CREATE_FAILED;
    }

    strncpy(ifr.ifr_name, can_name, sizeof(ifr.ifr_name));
    if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
        printf("ioctl(%d) falied, errno: %d\n", fd, errno);
        perror("ioctl(%d) error");
        close(fd);
        return STATE_PORT_NETWORK_SOCKET_CONFIG_FAILED;
    }

    /*
    if (setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0) < 0) {
        printf("setsockopt(%d) falied, errno: %d\n", fd, errno);
        perror("setsockopt(%d) error");
        close(fd);
        return STATE_PORT_NETWORK_SOCKET_CONFIG_FAILED;
    } */

    addr.can_family = family;
    addr.can_ifindex = ifr.ifr_ifindex;
    if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_can)) < 0) {
        printf("bind(%d) falied, errno: %d\n", fd, errno);
        perror("bind(%d) error");
        close(fd);
        return STATE_PORT_NETWORK_SOCKET_BIND_FAILED;
    }

    *fd_out = fd;
    printf("success to establish socketcan, fd=%d\n", fd);

    return STATE_SUCCESS;
}

static int32_t controller_send_data(controller_handle_t *p_handle, struct can_frame *frame_data)
{
	int32_t res = STATE_SUCCESS;
	ssize_t send_bytes = 0;
    struct pollfd pfd;

    memset(&pfd, 0, sizeof(pfd));
    pfd.fd = p_handle->fd;
    pfd.events |= POLLOUT;
	
	res = poll(&pfd, 1, p_handle->send_timeout_ms);
    if (res < 0) {
		printf("controller_send_data, errno: %d\n", errno);
		res = STATE_PORT_NETWORK_POLL_FAILED;
    } else if (res == 0) {
		res = STATE_CCAN_SEND_NOT_FINISHED;
    } else {
        if (((uint32_t)pfd.revents & (uint32_t)POLLOUT) == 0) {
            res = STATE_PORT_NETWORK_SEND_FAILED;
        } else {
            send_bytes = write(p_handle->fd, frame_data, sizeof(struct can_frame));
            if (send_bytes < 0) {
                res = STATE_PORT_NETWORK_SEND_FAILED;
            } else if ((size_t)send_bytes != sizeof(struct can_frame)) {
                res = STATE_SYS_DEPEND_NWK_WRITE_LESSDATA;
            }
        }
    }

    if (res < STATE_SUCCESS) {
		return res;
	}
	
	res = STATE_SUCCESS;

    return res;
}

int32_t controller_send(void *p_handle, struct can_frame *frame_data)
{
	int32_t res = STATE_SUCCESS;
    controller_handle_t *controller_handle = NULL;

    if (p_handle == NULL || frame_data == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    controller_handle = (controller_handle_t *)p_handle;

    res = controller_send_data(controller_handle, frame_data);

    return res;
}

static int32_t controller_recv_data(controller_handle_t *p_handle, struct can_frame *frame_data)
{
	int32_t res = STATE_SUCCESS;
	ssize_t recv_bytes = 0;
    struct pollfd pfd;

    memset(&pfd, 0, sizeof(pfd));
    pfd.fd = p_handle->fd;
    pfd.events |= POLLIN;

    res = poll(&pfd, 1, p_handle->recv_timeout_ms);
    if (res < 0) {
		printf("controller_recv_data, errno: %d\n", errno);
		res = STATE_PORT_NETWORK_POLL_FAILED;
    } else if (res == 0) {
		res = STATE_CCAN_RECV_NOT_FINISHED;
    } else {
        if (((uint32_t)pfd.revents & (uint32_t)POLLIN) == 0) {
            res = STATE_PORT_NETWORK_RECV_FAILED;
        } else {
            recv_bytes = read(p_handle->fd, frame_data, sizeof(struct can_frame));
            if (recv_bytes < 0) {
                res = STATE_PORT_NETWORK_RECV_FAILED;
            } else if ((size_t)recv_bytes != sizeof(struct can_frame)) {
                res = STATE_SYS_DEPEND_NWK_READ_LESSDATA;
            }
        }
    }
	
    if (res < STATE_SUCCESS) {
        return res;
	}

    res = STATE_CCAN_READ_FINISHED;

	return res;
}

int32_t controller_recv(void *p_handle)
{
    int32_t res = STATE_SUCCESS;
    uint64_t timenow_ms = 0;
    controller_handle_t *controller_handle = NULL;

    if (p_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    controller_handle = (controller_handle_t *)p_handle;

    timenow_ms = controller_time();
    while (1) {
        if (timenow_ms >= controller_time()) {
            timenow_ms = controller_time();
        }
        if (controller_time() - timenow_ms >= controller_handle->recv_timeout_ms) {
            res = STATE_CCAN_RECV_NOT_FINISHED;
            break;
        }

        res = controller_recv_data(controller_handle, &s_frame_data);
        if (res < STATE_SUCCESS) {
            break;
        }
    }

    if (res < STATE_SUCCESS) {
        if (res != STATE_CCAN_READ_FINISHED) {
            return res;
        } else {
            res = STATE_SUCCESS;
        }
    } else {
        return STATE_CCAN_RECV_NOT_FINISHED;
    }

    if (controller_handle->recv_handler != NULL) {
        controller_handle->recv_handler((void *)controller_handle, &s_frame_data);
    }

    return res;
}

int32_t controller_deinit(void *p_handle)
{
    controller_handle_t *controller_handle = NULL;

    if (p_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    controller_handle = (controller_handle_t *)p_handle;

    free(controller_handle);
    p_handle = NULL;

    return STATE_SUCCESS;
}
